The present invention relates to buffering audio data from a bursty source and reading out the buffered audio data for playback.
The technique of "double buffering" is a well known technique for receiving digital audio data from a bursty source. Initially, audio data words are received in sequence, and placed in successive locations in a first buffer memory. When this first buffer is full, subsequent data words are placed in successive locations in a second buffer memory. While the first buffer memory is being processed, the second buffer memory can be filled, and the data in the first buffer remains unchanged during processing. Similarly, when the processing proceeds to the second buffer, subsequent data words received are placed in successive locations of the first memory, over-writing the previous values. During this re-filling of the first buffer memory, the contents of the second buffer memory remain unchanged during processing. This back and forth processing is also sometimes called "ping-pong" buffering.
The technique of playing back continuous loops of audio from a memory loaded with audio data by a CPU is also well known, and is a common element of "wavetable" music synthesizers. It such synthesizers, digital audio data words are stored by the CPU in a memory. When this audio data is played back, two data word locations in memory are designated as the "start" and "loop" point in the memory. While looping, when the location in memory immediately preceding the loop point is played back, the playback then skips back to play the start point, which is prior to the loop point in memory. Thus the sound between the start and loop points is repeated. This typically occurs for the duration that a musician depresses the synthesizer keyboard key designating that sound.
The algorithm by which looping is performed is generally described as follows: For each output sample, the playback address (which has both an integer and a fractional part) is increased by the phase increment (which also has an integer and fractional part). The value of the phase increment determines the playback pitch and playback sample rate of the sound. If the phase increment is exactly one, the sound is played back at its original pitch and sample rate, and output samples are played back in sequence. When looping is active, then after the phase increment is added to the playback address, the resulting sum is compared to the loop address. If the resulting sum is equal to or greater than the loop address, then the loop address is subtracted from the resulting sum and the start address is added to the result of that subtraction to form the new playback address. If the comparison indicates that the resulting sum is less than the loop address, then the resulting sum (which is the old playback address plus the phase increment) becomes then new playback address. This algorithm is repeated for successive samples. FIG. 1 depicts such a loop 100 which begins at a loop start address (LSA) 102 and ends at a loop end address (LEA) 104. A current address (CA) 106 and a next current address (CA') 108 are separated by a phase increment (PI) 110.
This algorithm is implemented simply in hardware by utilizing a single register with an enable, and an adder with a carry input and a carry output. The register contains the playback address. In a first step, the adder adds the phase increment to the register with the carry input negated, and the result is stored back in the register, so that the register now contains the sum of the previous playback address and the phase increment.
In a second step, the one's complement of the loop address is added to the register, with the carry input asserted. The carry output of this addition is examined. If the carry output is negated, indicating that the register value does not equal or exceed the loop address, then the register is disabled and remains unchanged. If the carry output is asserted, indicating that the register value equals or exceeds the loop address, then the register is enabled and acquires the output of the adder, which is the register value less the loop address.
In a third step, the register is added to the start address with the carry input negated. If the carry output was asserted during the previous step, then the register is enabled and acquires the result of this addition. If the carry output was negated during the previous step, then the register is disabled and remains unchanged. At this point, the looping algorithm is complete and the register contains the next playback address. This algorithm is performed once each output sample period for each audio channel. Note that the CA is updated by the algorithm, and that the LSA and LEA are only altered by the controlling CPU.
Those skilled in the art will easily perceive that the three steps above can be implemented in combination with other steps also using the same adder, and that the register can also be shared during other steps, and loaded from and saved to a memory as necessary to implement the above steps.
The assertion or negation of the carry output during the second step indicates if a loop occurred during the cycle. This can be used as the basis of an interrupt to a CPU which will occur once per loop at the time the playback address returned to the start point. Thus, wavetable looping has a very efficient and compact hardware implementation.
It would be useful to combine wavetable looping techniques with double buffering so that audio data received from bursty sources such as CD-ROM may be played back in wavetable fashion. However, there is a fundamental incompatibility between the typical wavetable looping scheme and the requirements of double buffering.
Assume that audio data is being played back from a double buffer system in accordance with conventional wavetable techniques. Audio data is currently being read out from the first buffer while the second buffer is being filled with data. However, soon playback will switch to the second buffer to allow filling of the first buffer. To prepare for the switch, the LSA is set at the beginning of the second buffer, and the LEA is set at the location just subsequent to the end of the first buffer in memory. CA' is compared to LEA after each addition of PI. When the loop occurs, i.e., CA' equals or exceeds LEA, playback jumps to the beginning of the now full second buffer, and the first buffer is filled with data while the data already loaded into the second buffer is being processed.
However, the above steps will only occur properly only if the second buffer is below the first buffer in the address space they share. If this were not the case, then on the cycle following the switchover, the comparison of CA to LEA would cause a false jump to an invalid location because the modification of LEA requires more time than the jump of CA. This is because the modification of LEA is directed by the CPU in response to an interrupt caused by the comparison a result that has an associated latency which can be longer than an output sample period. However, if the second buffer is below the first buffer in their shared address space, the switch from the second buffer to the first buffer will not occur correctly for the same reason. In other words, jumping from an LEA upward in memory is precluded by the conventional wave table looping algorithm.
One known solution to this problem is to provide distinct loop end address registers for each buffer and switch between them whenever switching between buffers. However, an important drawback is that an extra register is required, increasing the area and complexity of the playback circuitry. For example, in the context of a 64 voice playback system, 64 additional registers and accompanying interconnections would be needed.
Another solution is to position the buffers adjacent to each other in their shared memory space so that there is never a gap between them. After a jump to the buffer that is higher in address space, the loop end address and loop start address will both be equivalent to the boundary between the two buffers. When the looping condition is now met and the loop start address is added to the difference between the current address and the loop end address, the result is again the current address since the loop end address and loop start address are equivalent. Thus, the false looping condition does not cause a jump. However, the CPU that responds to interrupts caused by the satisfaction of the looping condition must be able to distinguish between interrupts that are and are not associated with actual jumps. Also, the need to maintain the buffers exactly adjacent to one another reduces system flexibility.
What is needed is an efficient system for combining double buffering and wavetable looping that avoids the above drawbacks by allowing an upward jump at the end of the loop.